#include "Pile_List.h"
#include "Sequence.h"

#include "Log.h"
#include "PS_Log.h"

Pile_List::Pile_List() {
    // std::cout << "Pile List::ctor\n";
    // std::cout.flush();

    items = new std::list<Item*>();
    first = items->begin();
    last = first;

    // std::cout << "Pile List::ctor. made linked list\n";
    // std::cout.flush();

}

Pile_List::Pile_List(Pile_List& superset,
                     Item_List::iterator first,
                     Item_List::iterator last,
                     int n) {
    items = superset.items;
    this->first = first;
    this->last = last;
    this->n = n;
}

Pile_List::~Pile_List() {
    // std::cout << "Pile_List::~dctor\n";
    // std::cout.flush();

    items->clear();
}

void Pile_List::add(Item *item) {
    bool was_empty = items->empty();
    items->push_back(item);
    if (was_empty)
        first = last = items->begin();
    else
        last++;
    n++;

    LOG(INPUT) << "appended.  last:";
    (*last)->print(*Log::output);
}

//
// Move one block of items (the source) just past the end of another
// (the destination), in the linked list of items.
//
void Pile_List::move_block(Pile_List *source,
                           Pile_List *destination) {
    LOG_ENTER(OBJECT,"Pile_List::move_block");
    LOG(OBJECT) << "source:";
    source->write_IDs(*Log::output); (*Log::output) << "\n";
    LOG(OBJECT) << "destination:";
    destination->write_IDs(*Log::output); (*Log::output) << "\n";

    Item_List::iterator dst       = destination->last;
    dst++;  // insert just after destination.last
    Item_List::iterator src_first = source->first;
    Item_List::iterator src_last  = source->last;
    src_last++; // must be one past the end

    LOG(OBJECT) << "pre-splice:"
                << " dst=ID " << (*dst)->ID
                << " src_first=ID " << (*src_first)->ID
                << " src_last=ID " << (*src_last)->ID;

    // Check if spans already adjacent on list.
    // If so, don't splice, 'cause I think list::splice has a bug here.
    bool doSplice = (*dst != *src_first);
    if (doSplice) {
        items->splice(dst, *items, src_first, src_last);
    }

    LOG_EXIT();
}

int Pile_List::size() {
    return n;
}

void Pile_List::clear() {
    LOG_ENTER(OBJECT,"Pile_List::clear");

    // std::cout << "Pile::clear\n";
    // std::cout.flush();

    Item_List::iterator end = last;
    end++;
    items->erase(first, end);

    LOG_EXIT();
}

Iterator<Item*> *Pile_List::new_iterator() {
    Item_List::iterator begin = first;
    Item_List::iterator end = last;
    end++;
    return new Iterator_List<Item*>(begin, end);
}

void Pile_List::partition(Categorizer& cat,
                          std::vector<Pile*>& parts) {

    std::vector<Item_List::iterator> firsts;
    std::vector<Item_List::iterator> lasts;
    std::vector<int> ns;
    partition_sequentially<Item_List>(cat, first, n, firsts, lasts, ns);

    parts.clear();
    for (int i = 0; i < firsts.size(); ++i) {
        parts.push_back(new Pile_List(*this, firsts[i], lasts[i], ns[i]));
    }
}

void Pile_List::add(Pile *other) {
    if ((void*)(((Pile_List*)other)->items) == (void*)(this->items)) {
        // this and other are sharing the same linked list.
        // move the block of items in other next to the end
        // of the block of items in this.
        move_block((Pile_List*)other, this);
    }
    else {
        // Different Piles.  Append items one at a time.
        Iterator<Item*> *iter = other->new_iterator();
        while (iter->has_next())
            add(iter->get_next());
        delete iter;
    }
}
